#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace Weazel.Gezel.Model
{
  /// <summary>
  /// The model is the top-level element of the simulation which contains
  /// all controllers, datapaths, ip-blocks and systems declared. Since the
  /// model is the root element and parent to all other entities, it has a
  /// null parent.
  /// </summary>
  public partial class Model : AbstractEntity<CommonEntity>
  {
    /// <summary>
    /// The list of ip-blocks hashed on local name.
    /// </summary>
    protected Dictionary<string, IPBlock> ipBlocks;
    public Dictionary<string, IPBlock> IPBlocks
    {
      get
      {
        return ipBlocks;
      }
    }

    /// <summary>
    /// The list of all kinds of controllers hashed
    /// on local name.
    /// </summary>
    protected Dictionary<string, Controller> controllers;
    public Dictionary<string, Controller> Controllers
    {
      get
      {
        return controllers;
      }
    }

    /// <summary>
    /// The list of systems in the simulation
    /// hashed on local name.
    /// </summary>
    protected Dictionary<string, system> systems;
    public Dictionary<string, system> Systems
    {
      get
      {
        return systems;
      }
    }

    /// <summary>
    /// The list of datapaths in the simulation
    /// hashed on local name.
    /// </summary>
    protected Dictionary<string, Datapath> datapaths;
    public Dictionary<string, Datapath> Datapaths
    {
      get
      {
        return datapaths;
      }
    }

    /// <summary>
    /// This map contais constants
    /// </summary>
    private Dictionary<string, LeafExpression> constants;
    public Dictionary<string, LeafExpression> Constants
    {
      get
      {
        return constants;
      }
    }

    /// <summary>
    /// This map maps file names to tracers. The target of a tracer
    /// should be unique across the entire model.
    /// </summary>
    private Dictionary<string, SignalTrace> fileToTracerMap;
    public Dictionary<string, SignalTrace> FileToTracerMap
    {
      get
      {
        return fileToTracerMap;
      }

      set
      {
        fileToTracerMap = value;
      }
    }

    private List<OptionDirective> optionDirectives;
    public List<OptionDirective> OptionDirectives
    {
      get 
      { 
        return optionDirectives; 
      }
      set 
      { 
        optionDirectives = value; 
      }
    }

		public Block GetBlock(string name)
		{
			if (datapaths.ContainsKey(name))
				return datapaths[name];
			else if (ipBlocks.ContainsKey(name))
				return ipBlocks[name];
			else
				return null;
		}

    /// <summary>
    /// Returns a list of datapath dependencies rooted
    /// at a specific (named) datapath. The list is sorted such that
    /// those with no dependencies are first in the list.
    /// </summary>
    /// <param name="datapath">Datapath root</param>
    /// <returns>List of datapath names sorted by dependencies</returns>
    public List<string> GetDependencyList(string datapath)
    {
      List<string> tempResult = new List<string>();

      // construct the list using recursion
      insertIntoDependencyList(ref tempResult, datapath);

      // the list may contain duplicates. Those are removed now ..
      List<string> result = new List<string>();

      while (tempResult.Count > 0)
      {
        // extract
        string s = tempResult[tempResult.Count - 1];
        tempResult.RemoveAt(tempResult.Count - 1);

        if (!tempResult.Contains(s))
          result.Add(s);
      }

      return result;
    }

    private void insertIntoDependencyList(ref List<string> result, string datapath)
    {
      // locate the datapath
      Datapath root = datapaths[datapath];

      // add to the result set
      result.Add(datapath);

      foreach (string datapathChildName in root.ChildDatapathRefs.Keys)
      {
        // note: the list may contain duplicates ..
        insertIntoDependencyList(ref result, datapathChildName);
      }

      foreach (string ipBlockChildName in root.ChildIpBlocks.Keys)
      {
        // ip blocks does not have any dependencies (that we know of)
        result.Add(ipBlockChildName);
      }
    }

		/// <summary>
		/// Get the controller of a datapath.
		/// </summary>
		/// <param name="datapath">local name of the datapath</param>
		/// <returns>controller associated with datapath</returns>
		public Controller GetController(string datapath)
		{
			if (datapaths[datapath].ClonedFromDatapath != null)
				datapath = datapaths[datapath].ClonedFromDatapath;

			foreach (Controller controller in controllers.Values)
				if (controller.DatapathRef == datapath)
					return controller;

			return null;
		}

    /// <summary>
    /// Constructs the model with the given name
    /// and a null parent since it is the top
    /// level entity.
    /// </summary>
    /// <param name="name">model name</param>
    public Model(string name) : base(null, name)
    {			
      ipBlocks = new Dictionary<string, IPBlock>();
      controllers = new Dictionary<string, Controller>();
      datapaths = new Dictionary<string, Datapath>();
      systems = new Dictionary<string, system>();
      fileToTracerMap = new Dictionary<string, SignalTrace>();
      optionDirectives = new List<OptionDirective>();
      constants = new Dictionary<string, LeafExpression>();
    }
  }
}